/*
 * Copyright (C) 2009 M. Hautle.
 * 
 * This file is part of TrackedBean.
 * 
 * TrackedBean is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free
 * Software Foundation, either version 3 of the License, or (at your option) any later version.
 * 
 * TrackedBean is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License along with TrackedBean. If not, see <http://www.gnu.org/licenses/>.
 */
package ch.trackedbean.common;

import java.lang.Thread.UncaughtExceptionHandler;
import ch.trackedbean.internal.*;

/**
 * Helper for obtaining the default {@link IExceptionHandler}.<br>
 * To define a own {@link IExceptionHandler} simply create a service provider file for it:<br>
 * Thus is a <code>META-INF/services/ch.trackedbean.common.IExceptionHandler</code> file containing the fully qualified name of your implementation.<br>
 * 
 * @author M. Hautle
 */
public class DefaultExceptionHandler {
    /** The default handler. */
    private static final IExceptionHandler HANDLER;

    /** {@link UncaughtExceptionHandler} delegating the call to {@link #HANDLER}. */
    private static final ThreadHandler THREAD_HANDLER;

    static {
        HANDLER = ServiceHelper.getService(IExceptionHandler.class, DefaultHandler.class);
        THREAD_HANDLER = new ThreadHandler(HANDLER);
    }

    /**
     * Hidden constructor.
     */
    private DefaultExceptionHandler() {
    }

    /**
     * Returns the default {@link IExceptionHandler} to use.
     * 
     * @return The default handler
     */
    public static IExceptionHandler getDefault() {
        return HANDLER;
    }

    /**
     * Sets the default {@link IExceptionHandler} as {@link UncaughtExceptionHandler} of the given thread.<br>
     * Simply calls {@link Thread#setUncaughtExceptionHandler(UncaughtExceptionHandler)} passing the default {@link IExceptionHandler} as argument.
     * 
     * @param t The thread
     */
    public static void setUncaughtExceptionHandler(Thread t) {
        t.setUncaughtExceptionHandler(THREAD_HANDLER);
    }

    /**
     * Sets the default {@link IExceptionHandler} as default {@link UncaughtExceptionHandler} for all threads.<br>
     * Simply calls {@link Thread#setDefaultUncaughtExceptionHandler(UncaughtExceptionHandler)} passing the default {@link IExceptionHandler} as argument.
     */
    public static void setUncaughtExceptionHandler() {
        Thread.setDefaultUncaughtExceptionHandler(THREAD_HANDLER);
    }

    /**
     * {@link UncaughtExceptionHandler} delegating the call to a {@link IExceptionHandler}.
     * 
     * @author M. Hautle
     */
    private static class ThreadHandler implements UncaughtExceptionHandler {
        /** The handler to use. */
        private final IExceptionHandler handler;

        /**
         * Default constructor.
         * 
         * @param handler The handler to use
         */
        ThreadHandler(IExceptionHandler handler) {
            this.handler = handler;
        }

        /**
         * {@inheritDoc}
         */
        @Override
        public void uncaughtException(Thread t, Throwable e) {
            handler.exceptionOccurred(e);
        }
    }

    /**
     * A simple default implementation of {@link IExceptionHandler}.
     * 
     * @author M. Hautle
     */
    public static class DefaultHandler implements IExceptionHandler {
        /**
         * The offset were to get the caller class name. Under OSGi our service is wrapped through a {@link ServiceWrapper} so the desired stack element is one
         * position later.
         */
        private static final int OFFSET = System.getProperty("osgi.os") != null ? 3 : 2;

        /**
         * {@inheritDoc}
         */
        @Override
        public void exceptionOccurred(Throwable e) {
            final StackTraceElement[] trace = Thread.currentThread().getStackTrace();
            final String owner = trace.length > 3 ? trace[OFFSET].getClassName() : "";
            throw new RuntimeException("Exception occoured in " + owner, e);
        }
    }
}
